﻿Imports System.Text
Imports System.Threading
Imports System.IO 'I can get rid of this after debugging

Public Class Worker
    Private m_thMain As System.Threading.Thread 'A reference to the main thread.
    Private m_booMustStop As Boolean = False 'Boolean value that signals to the service thread whether to stop or not.
    Private DC As DataCollector 'Created a variable here because we will use multiple DataCollectors using the new constructor later in the program
    Private ReadOnly myLock As New Object()
    Private Shared resetEvents As ManualResetEvent() 'variable so that we can signal from our children threads to our parent thread

    ''' <summary>
    ''' Method that stops the worker thread.
    ''' </summary>
    ''' <remarks>This method should stop the service in a safe manner by waiting to join the worker thread until it is done working.</remarks>
    Public Sub StopWork()
        Try
            'make sure that the stop parameter is true
            m_booMustStop = True
            SyncLock myLock
                Monitor.Pulse(myLock)
            End SyncLock

            'Check to make sure that we have a reference to the m_thMain
            'EventLog.WriteEntry("VOEIS Hub Push Service", "Getting ready to check the object state")
            If Not m_thMain Is Nothing Then
                'Set a 5 minute timeout on the Thread.Join() because I was getting null reference exceptions after the service was stopped before and it seems to have something to do with
                'this join timeout and trying to abort the thread.  It also seems to be in direct relation to the sleep time at the end of the while look in the DoWork() method.
                If Not m_thMain.Join(300000) Then
                    'Kill the thread
                    m_thMain.Abort()
                End If
            End If
        Catch ex As Exception
            'I hope that we never get here!
            System.Diagnostics.EventLog.WriteEntry("VOEIS Hub Push Service", "Error in Worker class." & Environment.NewLine & "StopWork() exception: " & ex.Message.ToString, EventLogEntryType.Error)
        End Try
    End Sub

    ''' <summary>
    ''' This is the main work thread for the service. 
    ''' </summary>
    ''' <remarks> We establish our arrays, set our children threads up, execute the push, and then recycle after the user specified interval.</remarks>
    Public Sub DoWork()
        m_thMain = System.Threading.Thread.CurrentThread
        Dim arylstNewRecords As New ArrayList 'arraylist to hold the table names with new records
        Dim arylstDoWorkArgs As ArrayList 'ArrayList for the ThreadPool that holds the table name and reset events
        Dim arylstDoWorkArgsWrapper As New ArrayList 'Wrapper array list to hold the array lists generated for the child workers
        'Dim str As String = "" 'String to hold table names for output
        Dim mailer As New NotifyMail()
        resetEvents = New ManualResetEvent(System.Environment.ProcessorCount - 1) {} 'Setup our ManualResetEvents array

        Try
            'Send a notification stating that the service has started.
            mailer.SendServiceMail("VOEIS Hub Push Service Started", "Informational message: VOEIS Hub Push Service Started at " & DateTime.Now() & " on agent " & System.Environment.MachineName.ToString() & "." & Environment.NewLine & "No further action required.")

            'Set up our thread pool for the current machine
            Threading.ThreadPool.SetMaxThreads((System.Environment.ProcessorCount - 1) * 2, (System.Environment.ProcessorCount - 1) * 2)

            'Run the service until we receive a stop command
            While Not m_booMustStop
                ' Find out how many threads I want to start (i.e. how many projects am I going to have to push?)
                ' I will do this by running a stored procedure/query against the view tables which are linked to the VOEIS Hub metadata table

                'reset our string
                'str = ""

                'Get an arraylist of the datasets that have new records
                arylstNewRecords = DataMonitor.ListNewData

                'I think that this section is just a remnant from testing, so I am going to comment it out.
                'For i As Integer = 0 To arylstNewRecords.Count - 1
                '    str += arylstNewRecords(i) & Environment.NewLine
                'Next

                'Next, I want to start worker threads that will get the new data, cURL it to the VOEIS Hub, update the metadata in the database, report that they're done, and then quit
                If arylstNewRecords.Count > 0 Then
                    'Reset the resetEvents array to avoid out of bounds errors
                    ReDim resetEvents(arylstNewRecords.Count - 1)

                    'Clear the wrapper class and trim it 
                    arylstDoWorkArgsWrapper.Clear()
                    arylstDoWorkArgsWrapper.TrimToSize()

                    'Cycle through the records if there are any
                    For i = 0 To arylstNewRecords.Count - 1

                        'Create a new DataCollector for the thread
                        DC = New DataCollector

                        'Create a new reset event for this thread so it can signal it's completion
                        resetEvents(i) = New System.Threading.ManualResetEvent(False)
                        resetEvents(i).Reset()

                        'Initialize the doworkargs array list to be passed to the child thread
                        arylstDoWorkArgs = New ArrayList
                        arylstDoWorkArgs.Add(arylstNewRecords(i))
                        arylstDoWorkArgs.Add(resetEvents(i))

                        'Add the DoWorkArgs array list to the wrapper so we don't run into 'shared' issues
                        arylstDoWorkArgsWrapper.Add(arylstDoWorkArgs)

                        'Queue the work item
                        Threading.ThreadPool.QueueUserWorkItem(New System.Threading.WaitCallback(AddressOf DC.DoWork), arylstDoWorkArgsWrapper(i))
                    Next

                    System.Diagnostics.EventLog.WriteEntry("VOEIS Hub Push Service", resetEvents.Count.ToString & " push threads in process.  Service thread sleeping for " & My.Settings.DataCheckInterval.ToString & " milliseconds, or until all operations are complete.")
                Else
                    System.Diagnostics.EventLog.WriteEntry("VOEIS Hub Push Service", "No new records available service sleeping for " & My.Settings.DataCheckInterval.ToString & " ms.")

                    'Create a reset event so we don't get a NullReferenceException
                    ReDim resetEvents(0)
                    resetEvents(0) = New System.Threading.ManualResetEvent(True)
                End If

                'TODO: Put a check here to see if we are actually getting to this point before the streams are done, or if we are being hung up in the above block of code.

                'Pause the thread until the next interval (set in my.settings)
                SyncLock myLock
                    Monitor.Wait(myLock, My.Settings.DataCheckInterval)
                End SyncLock

                'Here I need to check that all of the threads have completed before moving back to the top of the loop
                System.Threading.WaitHandle.WaitAll(resetEvents)

            End While
        Catch ex As Exception
            'There was a problem, so write to the event log and stop the service
            System.Diagnostics.EventLog.WriteEntry("VOEIS Hub Push Service", "Error in Worker class." & Environment.NewLine & ex.Message.ToString & Environment.NewLine & ex.InnerException.ToString, EventLogEntryType.Error)
            StopWork()
        End Try
    End Sub
End Class
